﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;

namespace HoNSharp
{
    public delegate void HoNLoginEventHandler(object sender, HoNLoginResultArgs e);
    public delegate void HoNAuthAcceptedEventHandler(object sender, HoNAuthAcceptedArgs e);
    public delegate void HoNPingEventHandler(object sender, HoNPingArgs e);
    public delegate void HoNChannelMessageEventHandler(object sender, HoNChannelMessageArgs e);
    public delegate void HoNChannelEmoteEventHandler(object sender, HoNChannelEmoteArgs e);
    public delegate void HoNRollDiceEventHandler(object sender, HoNRollDiceArgs e);
    public delegate void HoNChangedChannelEventHandler(object sender, HoNChangedChannelArgs e);
    public delegate void HoNJoinedChannelEventHandler(object sender, HoNJoinedChannelArgs e);
    public delegate void HoNLeftChannelEventHandler(object sender, HoNLeftChannelArgs e);
    public delegate void HoNUpdateStatusEventHandler(object sender, HoNUpdateStatusArgs e);
    public delegate void HoNNotificationEventHandler(object sender, HoNNotificationArgs e);
    public delegate void HoNIMEventHandler(object sender, HoNIMArgs e);
    public delegate void HoNMaxChannelsEventHandler(object sender, HoNNoMoreChannelsArgs e);
    public delegate void HoNUserInfoNoExistEventHandler(object sender, HoNUserInfoNoExistArgs e);
    public delegate void HoNUserInfoOfflineEventHandler(object sender, HoNUserInfoOfflineArgs e);
    public delegate void HoNUserInfoOnlineEventHandler(object sender, HoNUserInfoOnlineArgs e);
    public delegate void HoNUserInfoInGameEventHandler(object sender, HoNUserInfoInGameArgs e);
    public delegate void HoNChannelKickEventHandler(object sender, HoNChannelKickArgs e);
    public delegate void HoNChannelSilencedEventHandler(object sender, HoNChannelSilencedArgs e);
    public delegate void HoNChannelSilenceLiftedEventHandler(object sender, HoNChannelSilenceLiftedArgs e);
    public delegate void HoNChannelSilencePlacedEventHandler(object sender, HoNChannelSilencePlacedArgs e);
    public delegate void HoNChannelPromoteEventHandler(object sender, HoNChannelPromoteArgs e);
    public delegate void HoNChannelDemoteEventHandler(object sender, HoNChannelDemoteArgs e);
    public delegate void HoNJoinChannelPasswordEventHandler(object sender, HoNJoinChannelPasswordArgs e);
    public delegate void HoNInitialStatusEventHandler(object sender, HoNInitialStatusArgs e);
    public delegate void HoNWhisperEventHandler(object sender, HoNWhisperArgs e);
    public delegate void HoNBuddyWhisperEventHandler(object sender, HoNBuddyWhisperArgs e);
    public delegate void HoNClanWhisperEventHandler(object sender, HoNClanWhisperArgs e);
    public delegate void HoNChannelListEventHandler(object sender, HoNChannelListArgs e);
    public delegate void HoNMessageAllEventHandler(object sender, HoNMessageAllArgs e);
    public delegate void HoNVersionMismatchEventHandler(object sender, EventArgs e);
    public delegate void HoNWhisperFailedEventHandler(object sender, EventArgs e);
    public delegate void HoNClanInviteEventHandler(object sender, HoNClanInviteArgs e);
    public delegate void HoNNewClanMemberEventHandler(object sender, HoNNewClanMemberArgs e);
    public delegate void HoNLookingForClanEventHandler(object sender, HoNLookingForClanArgs e);
    public delegate void HoNChannelTopicEventHandler(object sender, HoNChannelTopicArgs e);
    public delegate void HoNChannelUpdateEventHandler(object sender, HoNChannelUpdateArgs e);
    public delegate void HoNInvitedToServerEventHandler(object sender, HoNInvitedToServerArgs e);
    public delegate void HoNListedChannelEventHandler(object sender, HoNListedChannelArgs e);
    public delegate void HoNNameChangeEventHandler(object sender, HoNNameChangeArgs e);
    public delegate void HoNUserCountUpdateEventHandler(object sender, HoNUserCountUpdateArgs e);
    public delegate void HoNBannedFromChannelEventHandler(object sender, HoNBannedFromChannelArgs e);
    public delegate void HoNUnbannedFromChannelEventHandler(object sender, HoNUnbannedFromChannelArgs e);
    public delegate void HoNYouAreBannedFromChannelEventHandler(object sender, HoNYouAreBannedFromChannelArgs e);
    public delegate void HoNAuthDeniedEventHandler(object sender, HoNAuthDeniedArgs e);

    public class HoNLoginResultArgs : EventArgs
    {
        private bool _success;
        private string _message;

        public HoNLoginResultArgs(bool LoginSuccess, string LoginMessage)
        {
            this._success = LoginSuccess;
            this._message = LoginMessage;
        }

        public bool IsLoggedIn()
        {
            return this._success;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNAuthAcceptedArgs : EventArgs
    {

    }
    public class HoNPingArgs : EventArgs
    {

    }
    public class HoNChannelMessageArgs : EventArgs
    {
        private UInt32 _userid;
        private UInt32 _chatid;
        private string _message;

        public HoNChannelMessageArgs(UInt32 UserID, UInt32 ChatID, string Message)
        {
            this._userid = UserID;
            this._chatid = ChatID;
            this._message = Message;
        }

        public UInt32 GetUserID()
        {
            return this._userid;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNChannelEmoteArgs : EventArgs
    {
        private UInt32 _userid;
        private UInt32 _chatid;
        private string _message;

        public HoNChannelEmoteArgs(UInt32 UserID, UInt32 ChatID, string Message)
        {
            this._userid = UserID;
            this._chatid = ChatID;
            this._message = Message;
        }

        public UInt32 GetUserID()
        {
            return this._userid;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNRollDiceArgs : EventArgs
    {
        private UInt32 _userid;
        private UInt32 _chatid;
        private string _message;

        public HoNRollDiceArgs(UInt32 UserID, UInt32 ChatID, string Message)
        {
            this._userid = UserID;
            this._chatid = ChatID;
            this._message = Message;
        }

        public UInt32 GetUserID()
        {
            return this._userid;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNChangedChannelArgs : EventArgs
    {
        private string _channel;
        private UInt32 _chatid;
        private string _topic;
        private UInt32 _numops;
        private UInt32 _numusers;
        private HoNOper[] _opers;
        private HoNUser[] _users;

        public HoNChangedChannelArgs(string Channel, UInt32 ChatID, string Topic)
        {
            this._channel = Channel;
            this._chatid = ChatID;
            this._topic = Topic;
        }

        public void SetNumOpers(UInt32 NumberOfOperators)
        {
            this._numops = NumberOfOperators;
            this._opers = new HoNOper[NumberOfOperators];
        }

        public void SetNumUsers(UInt32 NumberOfUsers)
        {
            this._numusers = NumberOfUsers;
            this._users = new HoNUser[NumberOfUsers];
        }

        public void AddOper(int Index, UInt32 OperID, byte OperType)
        {
            this._opers[Index].OperID = OperID;
            this._opers[Index].OperType = OperType;
        }

        public void AddUser(int Index, string Username, UInt32 UserID, byte Status, byte Flags)
        {
            this._users[Index].Username = Username;
            this._users[Index].UserID = UserID;
            this._users[Index].Status = Status;
            this._users[Index].Flags = Flags;
        }

        public string GetChannel()
        {
            return this._channel;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetTopic()
        {
            return this._topic;
        }

        public UInt32 GetNumberOfOpers()
        {
            return this._numops;
        }

        public UInt32 GetNumberOfUsers()
        {
            return this._numusers;
        }

        public HoNOper[] GetOpers()
        {
            return this._opers;
        }

        public HoNUser[] GetUsers()
        {
            return this._users;
        }
    }
    public class HoNJoinedChannelArgs : EventArgs
    {
        private UInt32 _chatid;
        private HoNUser _user;

        public HoNJoinedChannelArgs(string Username, UInt32 UserID, UInt32 ChatID, byte Status, byte Flags)
        {
            this._user.Username = Username;
            this._user.UserID = UserID;
            this._user.Status = Status;
            this._user.Flags = Flags;
            this._chatid = ChatID;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public HoNUser GetUser()
        {
            return this._user;
        }
    }
    public class HoNLeftChannelArgs : EventArgs
    {
        private UInt32 _userid;
        private UInt32 _chatid;

        public HoNLeftChannelArgs(UInt32 UserID, UInt32 ChatID)
        {
            this._userid = UserID;
            this._chatid = ChatID;
        }

        public UInt32 GetUserID()
        {
            return this._userid;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }
    }
    public class HoNUpdateStatusArgs : EventArgs
    {
        private UInt32 _userid;
        private byte _status;
        private byte _flags;
        private UInt32 _clanid;
        private string _clan;
        private string _gameserver;
        private string _gamename;
        private UInt32 _gameid;

        public HoNUpdateStatusArgs(UInt32 UserID, byte Status, byte Flags, UInt32 ClanID, string Clan, string GameServer, string GameName, UInt32 GameID)
        {
            this._userid = UserID;
            this._status = Status;
            this._flags = Flags;
            this._clanid = ClanID;
            this._clan = Clan;
            this._gameserver = GameServer;
            this._gamename = GameName;
            this._gameid = GameID;
        }

        public UInt32 GetUserID()
        {
            return this._userid;
        }

        public byte GetStatus()
        {
            return this._status;
        }

        public byte GetFlags()
        {
            return this._flags;
        }

        public UInt32 GetClanID()
        {
            return this._clanid;
        }

        public string GetClan()
        {
            return this._clan;
        }

        public string GetGameServer()
        {
            return this._gameserver;
        }

        public string GetGameName()
        {
            return this._gamename;
        }

        public UInt32 GetGameID()
        {
            return this._gameid;
        }
    }
    public class HoNNotificationArgs : EventArgs
    {
        private byte _type;
        private string _notification;

        public HoNNotificationArgs(byte NotificationType, string Notification)
        {
            this._type = NotificationType;
            this._notification = Notification;
        }

        public byte GetNotificationType()
        {
            return this._type;
        }

        public string GetNotification()
        {
            return this._notification;
        }
    }
    public class HoNIMArgs : EventArgs
    {
        private string _username;
        private string _message;

        public HoNIMArgs(string Username, string Message)
        {
            this._username = Username;
            this._message = Message;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNNoMoreChannelsArgs : EventArgs
    {

    }
    public class HoNUserInfoNoExistArgs : EventArgs
    {
        private string _username;

        public HoNUserInfoNoExistArgs(string Username)
        {
            this._username = Username;
        }

        public string GetUsername()
        {
            return this._username;
        }
    }
    public class HoNUserInfoOfflineArgs : EventArgs
    {
        private string _username;
        private string _lastseen;

        public HoNUserInfoOfflineArgs(string Username, string LastSeen)
        {
            this._username = Username;
            this._lastseen = LastSeen;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public string GetLastSeen()
        {
            return this._lastseen;
        }
    }
    public class HoNUserInfoOnlineArgs : EventArgs
    {
        private string _username;
        private UInt32 _numchannels;
        private string[] _channels;

        public HoNUserInfoOnlineArgs(string Username, UInt32 NumberOfChannels)
        {
            this._username = Username;
            this._numchannels = NumberOfChannels;
            this._channels = new string[NumberOfChannels];
        }

        public void AddChannel(int Index, string Channel)
        {
            this._channels[Index] = Channel;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public UInt32 GetNumberOfChannels()
        {
            return this._numchannels;
        }

        public string[] GetChannels()
        {
            return this._channels;
        }
    }
    public class HoNUserInfoInGameArgs : EventArgs
    {
        private string _username;
        private string _gamename;
        private string _gametime;

        public HoNUserInfoInGameArgs(string Username, string GameName, string GameTime)
        {
            this._username = Username;
            this._gamename = GameName;
            this._gametime = GameTime;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public string GetGameName()
        {
            return this._gamename;
        }

        public string GetGameTime()
        {
            return this._gametime;
        }
    }
    public class HoNChannelKickArgs : EventArgs
    {
        private UInt32 _chatid;
        private UInt32 _kickerid;
        private UInt32 _kickedid;

        public HoNChannelKickArgs(UInt32 ChatID, UInt32 KickerID, UInt32 KickedID)
        {
            this._chatid = ChatID;
            this._kickerid = KickerID;
            this._kickedid = KickedID;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public UInt32 GetKickerID()
        {
            return this._kickerid;
        }

        public UInt32 GetKickedID()
        {
            return this._kickedid;
        }
    }
    public class HoNChannelSilencedArgs : EventArgs
    {
        private UInt32 _chatid;

        public HoNChannelSilencedArgs(UInt32 ChatID)
        {
            this._chatid = ChatID;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }
    }
    public class HoNChannelSilenceLiftedArgs : EventArgs
    {
        private string _channel;

        public HoNChannelSilenceLiftedArgs(string Channel)
        {
            this._channel = Channel;
        }

        public string GetChannel()
        {
            return this._channel;
        }
    }
    public class HoNChannelSilencePlacedArgs : EventArgs
    {
        private string _channel;
        private string _muter;
        private string _muted;
        private UInt32 _duration;

        public HoNChannelSilencePlacedArgs(string Channel, string Muter, string Muted, UInt32 Duration)
        {
            this._channel = Channel;
            this._muter = Muter;
            this._muted = Muted;
            this._duration = Duration;
        }

        public string GetChannel()
        {
            return this._channel;
        }

        public string GetMuter()
        {
            return this._muter;
        }

        public string GetMuted()
        {
            return this._muted;
        }

        public UInt32 GetDuration()
        {
            return this._duration;
        }
    }
    public class HoNChannelPromoteArgs : EventArgs
    {
        private UInt32 _chatid;
        private UInt32 _targetid;
        private UInt32 _operid;

        public HoNChannelPromoteArgs(UInt32 ChatID, UInt32 TargetID, UInt32 OperID)
        {
            this._chatid = ChatID;
            this._targetid = TargetID;
            this._operid = OperID;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public UInt32 GetTargetID()
        {
            return this._targetid;
        }

        public UInt32 GetOperID()
        {
            return this._operid;
        }
    }
    public class HoNChannelDemoteArgs : EventArgs
    {
        private UInt32 _chatid;
        private UInt32 _targetid;
        private UInt32 _operid;

        public HoNChannelDemoteArgs(UInt32 ChatID, UInt32 TargetID, UInt32 OperID)
        {
            this._chatid = ChatID;
            this._targetid = TargetID;
            this._operid = OperID;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public UInt32 GetTargetID()
        {
            return this._targetid;
        }

        public UInt32 GetOperID()
        {
            return this._operid;
        }
    }
    public class HoNJoinChannelPasswordArgs : EventArgs
    {
        private string _channel;

        public HoNJoinChannelPasswordArgs(string Channel)
        {
            this._channel = Channel;
        }

        public string GetChannel()
        {
            return this._channel;
        }
    }
    public class HoNInitialStatusArgs : EventArgs
    {
        private UInt32 _numBuddies;
        private HoNBuddy[] _buddies;

        public HoNInitialStatusArgs(UInt32 NumberOfBuddies)
        {
            this._numBuddies = NumberOfBuddies;
            this._buddies = new HoNBuddy[NumberOfBuddies];
        }

        public void AddBuddy(int Index, UInt32 UserID, byte Status, byte Flags, string Server, string GameName)
        {
            this._buddies[Index].UserID = UserID;
            this._buddies[Index].Status = Status;
            this._buddies[Index].Flags = Flags;
            this._buddies[Index].Server = Server;
            this._buddies[Index].GameName = GameName;
        }

        public UInt32 GetBuddyCount()
        {
            return this._numBuddies;
        }

        public HoNBuddy[] GetBuddies()
        {
            return this._buddies;
        }
    }
    public class HoNWhisperArgs : EventArgs
    {
        private string _username;
        private string _message;

        public HoNWhisperArgs(string Username, string Message)
        {
            this._username = Username;
            this._message = Message;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNBuddyWhisperArgs : EventArgs
    {
        private string _username;
        private string _message;

        public HoNBuddyWhisperArgs(string Username, string Message)
        {
            this._username = Username;
            this._message = Message;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNClanWhisperArgs : EventArgs
    {
        private UInt32 _userid;
        private string _message;

        public HoNClanWhisperArgs(UInt32 UserID, string Message)
        {
            this._userid = UserID;
            this._message = Message;
        }

        public UInt32 GetUserID()
        {
            return this._userid;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNChannelListArgs : EventArgs
    {
        private UInt32 _channelcount;
        private HoNChannel[] _channels;

        public HoNChannelListArgs(UInt32 ChannelCount)
        {
            this._channelcount = ChannelCount;
            this._channels = new HoNChannel[ChannelCount];
        }

        public void AddChannel(int Index, UInt32 ChatID, string Channel, UInt32 UserCount)
        {
            this._channels[Index].ChatID = ChatID;
            this._channels[Index].Channel = Channel;
            this._channels[Index].UserCount = UserCount;
        }

        public UInt32 GetChannelCount()
        {
            return this._channelcount;
        }

        public HoNChannel[] GetChannels()
        {
            return this._channels;
        }
    }
    public class HoNMessageAllArgs : EventArgs
    {
        string _username;
        string _message;

        public HoNMessageAllArgs(string Username, string Message)
        {
            this._username = Username;
            this._message = Message;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public string GetMessage()
        {
            return this._message;
        }
    }
    public class HoNClanInviteArgs : EventArgs
    {
        private string _username;
        private string _clan;

        public HoNClanInviteArgs(string Username, string Clan)
        {
            this._username = Username;
            this._clan = Clan;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public string GetClan()
        {
            return this._clan;
        }
    }
    public class HoNNewClanMemberArgs : EventArgs
    {
        private UInt32 _userID;

        public HoNNewClanMemberArgs(UInt32 UserID)
        {
            this._userID = UserID;
        }

        public UInt32 GetUserID()
        {
            return this._userID;
        }
    }
    public class HoNLookingForClanArgs : EventArgs
    {
        private string _username;

        public HoNLookingForClanArgs(string Username)
        {
            this._username = Username;
        }

        public string GetUsername()
        {
            return this._username;
        }
    }
    public class HoNChannelTopicArgs : EventArgs
    {
        private UInt32 _chatid;
        private string _topic;

        public HoNChannelTopicArgs(UInt32 ChatID, string Topic)
        {
            this._chatid = ChatID;
            this._topic = Topic;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetTopic()
        {
            return this._topic;
        }
    }
    public class HoNChannelUpdateArgs : EventArgs
    {
        private string _channel;
        private UInt32 _chatid;
        private string _topic;
        private HoNOper[] _opers;

        public HoNChannelUpdateArgs(string Channel, UInt32 ChatID, string Topic)
        {
            this._channel = Channel;
            this._chatid = ChatID;
            this._topic = Topic;
        }

        public void SetNumOpers(UInt32 NumberOfOperators)
        {
            this._opers = new HoNOper[NumberOfOperators];
        }

        public void AddOper(int Index, UInt32 OperID, byte OperType)
        {
            this._opers[Index].OperID = OperID;
            this._opers[Index].OperType = OperType;
        }

        public string GetChannel()
        {
            return this._channel;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetTopic()
        {
            return this._topic;
        }

        public HoNOper[] GetOpers()
        {
            return this._opers;
        }
    }
    public class HoNInvitedToServerArgs : EventArgs
    {
        private string _username;
        private UInt32 _userid;
        private string _server;

        public HoNInvitedToServerArgs(string Username, UInt32 UserID, string Server)
        {
            this._username = Username;
            this._userid = UserID;
            this._server = Server;
        }

        public string GetUsername()
        {
            return this._username;
        }

        public UInt32 GetUserID()
        {
            return this._userid;
        }

        public string GetServer()
        {
            return this._server;
        }
    }
    public class HoNListedChannelArgs : EventArgs
    {
        private UInt32 _chatid;
        private string _channel;
        private UInt16 _usercount;

        public HoNListedChannelArgs(UInt32 ChatID, string Channel, UInt16 UserCount)
        {
            this._chatid = ChatID;
            this._channel = Channel;
            this._usercount = UserCount;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetChannel()
        {
            return this._channel;
        }

        public UInt16 GetUserCount()
        {
            return this._usercount;
        }
    }
    public class HoNNameChangeArgs : EventArgs
    {
        private UInt32 _userid;
        private string _username;

        public HoNNameChangeArgs(UInt32 UserID, string Username)
        {
            this._userid = UserID;
            this._username = Username;
        }

        public UInt32 GetUserID()
        {
            return this._userid;
        }

        public string GetUsername()
        {
            return this._username;
        }
    }
    public class HoNUserCountUpdateArgs : EventArgs
    {
        private UInt32 _usercount;

        public HoNUserCountUpdateArgs(UInt32 UserCount)
        {
            this._usercount = UserCount;
        }

        public UInt32 GetUserCount()
        {
            return this._usercount;
        }
    }
    public class HoNBannedFromChannelArgs : EventArgs
    {
        private UInt32 _operid;
        private UInt32 _chatid;
        private string _username;

        public HoNBannedFromChannelArgs(UInt32 ChatID, UInt32 OperID, string Username)
        {
            this._operid = OperID;
            this._chatid = ChatID;
            this._username = Username;
        }

        public UInt32 GetOperID()
        {
            return this._operid;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetUsername()
        {
            return this._username;
        }
    }
    public class HoNUnbannedFromChannelArgs : EventArgs
    {
        private UInt32 _operid;
        private UInt32 _chatid;
        private string _username;

        public HoNUnbannedFromChannelArgs(UInt32 ChatID, UInt32 OperID, string Username)
        {
            this._operid = OperID;
            this._chatid = ChatID;
            this._username = Username;
        }

        public UInt32 GetOperID()
        {
            return this._operid;
        }

        public UInt32 GetChatID()
        {
            return this._chatid;
        }

        public string GetUsername()
        {
            return this._username;
        }
    }
    public class HoNYouAreBannedFromChannelArgs : EventArgs
    {
        private string _channel;

        public HoNYouAreBannedFromChannelArgs(string Channel)
        {
            this._channel = Channel;
        }

        public string GetChannel()
        {
            return this._channel;
        }
    }
    public class HoNAuthDeniedArgs : EventArgs
    {
        string _reason;

        public HoNAuthDeniedArgs(string reason)
        {
            this._reason = reason;
        }

        public string GetReason()
        {
            return this._reason;
        }
    }

    public struct HoNChannel
    {
        public UInt32 ChatID;
        public string Channel;
        public UInt32 UserCount;
    }

    public struct HoNOper
    {
        public UInt32 OperID;
        public byte OperType;
    }

    public struct HoNUser
    {
        public string Username;
        public UInt32 UserID;
        public byte Status;
        public byte Flags;
    }

    public struct HoNBuddy
    {
        public string Username;
        public UInt32 UserID;
        public byte Status;
        public byte Flags;
        public string Server;
        public string GameName;
    }

    public class HoNClient
    {
        public const string CLIENT_REQUEST_NICK2ID = "nick2id";
        public const string CLIENT_REQUEST_ID2NICK = "id2nick";
        public const string CLIENT_REQUEST_BANLIST_ADD = "new_banned";
        public const string CLIENT_REQUEST_BANLIST_DEL = "remove_banned";
        public const string CLIENT_REQUEST_BUDDY_ADD = "new_buddy";
        public const string CLIENT_REQUEST_BUDDY_DEL = "remove_buddy";
        public const string CLIENT_REQUEST_IGNORE_ADD = "new_ignored";
        public const string CLIENT_REQUEST_IGNORE_DEL = "remove_ignored";
        public const string CLIENT_REQUEST_STATS = "get_all_stats";
        public const string CLIENT_REQUEST_SERVER_LIST = "server_list";
        public const string CLIENT_REQUEST_ADD_ROOM = "add_room";
        public const string CLIENT_REQUEST_REMOVE_ROOM = "remove_room";

        //public const byte CHAT_CMD_AUTH_ACCEPTED =              0x00;	 // Identity validated and aceepted
        //public const byte CHAT_CMD_PING =                       0x01;     // Used to check if a user is alive
        //public const byte CHAT_CMD_PONG =                       0x02;     // Used to check if a user is alive
        public const byte CHAT_CMD_CHANNEL_MSG =                0x03;     // Used when a user messages a channel
        public const byte CHAT_CMD_CHANGED_CHANNEL =            0x04;     // Used when we change channels
        public const byte CHAT_CMD_JOINED_CHANNEL =             0x05;     // Used when a new user joins our channel
        public const byte CHAT_CMD_LEFT_CHANNEL =               0x06;     // Used when a user leaves our channel
        public const byte CHAT_CMD_DISCONNECTED =               0x07;     // Used when we get disconnected
        public const byte CHAT_CMD_WHISPER =                    0x08;     // Used when one user whispers another
        public const byte CHAT_CMD_WHISPER_FAILED =             0x09;     // Used when the whisper target could not be found
        public const byte CHAT_CMD_WEB_NOTIFICATION =           0x0A;     // Recieved from database when web-related event occurs
        public const byte CHAT_CMD_INITIAL_STATUS =             0x0B;     // Sent on connect to update buddy and clan connection status for new client
        public const byte CHAT_CMD_UPDATE_STATUS =              0x0C;     // Sent on connect to update buddy and clan connection status for old clients
        public const byte CHAT_CMD_BUDDY_ADD_NOTIFY =           0x0D;     // Sent with notification keys for the server to verify on buddy add
        public const byte CHAT_CMD_BUDDY_REMOVE_NOTIFY =        0x0E;     // Sent with notification keys for the server to verify on buddy remove
        public const byte CHAT_CMD_JOINING_GAME =               0x0F;     // Sent when a user starts joining a game
        public const byte CHAT_CMD_JOINED_GAME =                0x10;     // Sent when a user finishes joining a game
        public const byte CHAT_CMD_LEFT_GAME =                  0x11;     // Sent when a user leaves a game
        public const byte CHAT_CMD_NOTIFICATION =               0x12;     // Sent when the server has a notification for a client
        public const byte CHAT_CMD_CLAN_WHISPER =               0x13;     // Sent when whispering an entire clan
        public const byte CHAT_CMD_CLAN_WHISPER_FAILED =        0x14;     // Sent when a whisper to a clan fails
        public const byte CHAT_CMD_CLAN_PROMOTE_NOTIFY =        0x15;     // Sent with notification keys for the server to verify on clan promotion
        public const byte CHAT_CMD_CLAN_DEMOTE_NOTIFY =         0x16;     // Sent with notification keys for the server to verify on clan demotion
        public const byte CHAT_CMD_CLAN_REMOVE_NOTIFY =         0x17;     // Sent with notification keys for the server to verify on clan removal
        public const byte CHAT_CMD_LOOKING_FOR_CLAN =           0x18;     // Notification stating user is now "looking for clan"
        public const byte CHAT_CMD_NOT_LOOKING_FOR_CLAN =       0x19;     // Notification stating user is no longer "looking for clan"
        public const byte CHAT_CMD_MULT_LOOKING_FOR_CLAN =      0x1A;     // Notification stating multiple users are "looking for clan" = sent on connect)
        public const byte CHAT_CMD_FLOODING =                   0x1B;     // Warning to user that their message wasn't sent due to flood control
        public const byte CHAT_CMD_IM =                         0x1C;     // Used when a user recieves/sends an IM through the CC panel
        public const byte CHAT_CMD_IM_FAILED =                  0x1D;     // Used when a user fails to send an IM
        public const byte CHAT_CMD_JOIN_CHANNEL =               0x1E;     // Sent by user when joining a new channel
        //public const byte CHAT_CMD_CHANNEL_LIST =               0x1F;     // User requesting channel list or server sending channel list
        public const byte CHAT_CMD_WHISPER_BUDDIES =            0x20;     // Sending whisper to all buddies
        public const byte CHAT_CMD_MAX_CHANNELS =               0x21;     // Error sent when user has joined max. # of channels
        public const byte CHAT_CMD_LEAVE_CHANNEL =              0x22;     // Sent by user when leaving a channel
        public const byte CHAT_CMD_INVITE_USER_ID =             0x23;     // Sent by game server to invite a user to a game by account ID
        public const byte CHAT_CMD_INVITE_USER_NAME =           0x24;     // Sent by game server to invite a user to a game by account name
        public const byte CHAT_CMD_INVITED_TO_SERVER =          0x25;     // Sent by chat server to notify a user of a pending server invite
        public const byte CHAT_CMD_INVITE_FAILED_USER =         0x26;     // Notifies a user that their invite request failed because the target was not found
        public const byte CHAT_CMD_INVITE_FAILED_GAME =         0x27;     // Notifies a user that their invite request failed because they are not in a game
        public const byte CHAT_CMD_INVITE_REJECTED =            0x28;     // Indicates that a received invite was rejected
        public const byte CHAT_CMD_BANNED =                     0x29;     // User is/was banned for a duration
        public const byte CHAT_CMD_USER_INFO =                  0x2A;     // Returns information on a user
        public const byte CHAT_CMD_USER_INFO_NO_EXIST =         0x2B;     // The requested user does not exist
        public const byte CHAT_CMD_USER_INFO_OFFLINE =          0x2C;     // Returns information on an offline user
        public const byte CHAT_CMD_USER_INFO_ONLINE =           0x2D;     // Returns information on an online user
        public const byte CHAT_CMD_USER_INFO_IN_GAME =          0x2E;     // Returns information on a user in a game
        public const byte CHAT_CMD_CHANNEL_UPDATE =             0x2F;     // Update channel information
        public const byte CHAT_CMD_CHANNEL_TOPIC =              0x30;     // Set/get channel topic
        public const byte CHAT_CMD_CHANNEL_KICK =               0x31;     // Kick user from channel
        public const byte CHAT_CMD_CHANNEL_BAN =                0x32;     // Ban user from channel
        public const byte CHAT_CMD_CHANNEL_UNBAN =              0x33;     // Unban user from channel
        public const byte CHAT_CMD_CHANNEL_IS_BANNED =          0x34;     // User is banned from channel
        public const byte CHAT_CMD_CHANNEL_SILENCED =           0x35;     // User is silenced in this channel
        public const byte CHAT_CMD_CHANNEL_SILENCE_LIFTED =     0x36;     // User is no longer silenced in a channel
        public const byte CHAT_CMD_CHANNEL_SILENCE_PLACED =     0x37;     // User is now silenced in a channel
        public const byte CHAT_CMD_CHANNEL_SILENCE_USER =       0x38;     // Request to silence a user in a channel
        public const byte CHAT_CMD_MESSAGE_ALL =                0x39;     // Administrator message to all users
        public const byte CHAT_CMD_CHANNEL_PROMOTE =            0x3A;     // Request to promote a user in a channel
        public const byte CHAT_CMD_CHANNEL_DEMOTE =             0x3B;     // Request to demote a user in a channel
        public const byte CHAT_CMD_TRUNCATED_MESSAGE =          0x3C;     // Message is truncated, store for next packet's use
        public const byte CHAT_CMD_VERSION_MISMATCH =           0x3D;     // Client's version does not match the chat server
        public const byte CHAT_CMD_CHANNEL_SET_AUTH =           0x3E;     // User wants to enable authorization on a channel
        public const byte CHAT_CMD_CHANNEL_REMOVE_AUTH =        0x3F;     // User wants to disable authorization on a channel
        public const byte CHAT_CMD_CHANNEL_ADD_AUTH_USER =      0x40;     // User wants to add a user to the authorization list for a channel
        public const byte CHAT_CMD_CHANNEL_REM_AUTH_USER =      0x41;     // User wants to remove a user from the authorization list for a channel
        public const byte CHAT_CMD_CHANNEL_LIST_AUTH =          0x42;     // User wants to get the authorization list for a channel
        public const byte CHAT_CMD_CHANNEL_SET_PASSWORD =       0x43;     // User wants to set the password for a channel
        public const byte CHAT_CMD_CHANNEL_ADD_AUTH_FAIL =      0x44;     // Failed to add the user to the channel authorization list
        public const byte CHAT_CMD_CHANNEL_REM_AUTH_FAIL =      0x45;     // Failed to remove the user from the channel authorization list
        public const byte CHAT_CMD_JOIN_CHANNEL_PASSWORD =      0x46;     // Channel join with password
        public const byte CHAT_CMD_CLAN_ADD_MEMBER =            0x47;     // Request to add a new clan member
        public const byte CHAT_CMD_CLAN_ADD_REJECTED =          0x48;     // Request to add a member was rejected
        public const byte CHAT_CMD_CLAN_ADD_FAIL_ONLINE =       0x49;     // Request to add a member failed, user was not online
        public const byte CHAT_CMD_CLAN_ADD_FAIL_CLAN =         0x4A;     // Request to add a member failed, user is in a clan
        public const byte CHAT_CMD_CLAN_ADD_FAIL_INVITED =      0x4B;     // Request to add a member failed, user has already been invited
        public const byte CHAT_CMD_CLAN_ADD_FAIL_PERMS =        0x4C;     // Request to add a member failed, user does not have proper permissions
        public const byte CHAT_CMD_CLAN_ADD_FAIL_UNKNOWN =      0x4D;     // Request to add a member failed
        public const byte CHAT_CMD_NEW_CLAN_MEMBER =            0x4E;     // New user added to clan
        public const byte CHAT_CMD_CLAN_ADD_ACCEPTED =          0x4F;     // Request to add a member was accepted
        public const byte CHAT_CMD_CLAN_RANK_CHANGE =           0x50;     // Clan member's rank changed
        public const byte CHAT_CMD_CLAN_CREATE_REQUEST =        0x51;     // Create clan request
        public const byte CHAT_CMD_CLAN_CREATE_ACCEPT =         0x52;     // One of the founding members accepted the request
        public const byte CHAT_CMD_CLAN_CREATE_REJECT =         0x53;     // One of the founding members rejected the request
        public const byte CHAT_CMD_CLAN_CREATE_COMPLETE =       0x54;     // Clan creation completed successfully
        public const byte CHAT_CMD_CLAN_CREATE_FAIL_CLAN =      0x55;     // Clan creation failed, one or more users are already in a clan
        public const byte CHAT_CMD_CLAN_CREATE_FAIL_INVITE =    0x56;     // Clan creation failed, one or more users have an outstanding clan invitation
        public const byte CHAT_CMD_CLAN_CREATE_FAIL_FIND =      0x57;     // Clan creation failed, one or more users could not be found
        public const byte CHAT_CMD_CLAN_CREATE_FAIL_DUPE =      0x58;     // Clan creation failed, duplicate founding members
        public const byte CHAT_CMD_CLAN_CREATE_FAIL_PARAM =     0x59;     // Clan creation failed, one or more parameters are invalid
        public const byte CHAT_CMD_NAME_CHANGE =                0x5A;     // A user's name has changed
        public const byte CHAT_CMD_CLAN_CREATE_FAIL_NAME =      0x5B;     // Clan creation failed, name invalid
        public const byte CHAT_CMD_CLAN_CREATE_FAIL_TAG =       0x5C;     // Clan creation failed, tag invalid
        public const byte CHAT_CMD_CLAN_CREATE_FAIL_UNKNOWN =   0x5D;     // Clan creation failed, unknown error
        public const byte CHAT_CMD_ROLL_DICE =                  0x64;     // Used when a user rolls dice in the channel.
        public const byte CHAT_CMD_EMOTE =                      0x65;     // Used when a user sends a channel emote
        public const byte CHAT_CMD_USER_COUNT_UPDATE =          0x68;     // Tells you how many users are online.
        public const byte CHAT_CMD_INVALID =                    0xFE;     // Bad command
        //public const byte CHAT_CMD_AUTH_INFO =                  0xFF;     // Submitting identity for validation

        // Chat server version 5 constants
        /*public const ushort CHAT_CMD_UNKNOWN_0068 =                 0x0068;     // No idea what this is.
        public const ushort CHAT_CMD_AUTH_INFO =                    0x0C00;     // Submitting identity for validation
        public const ushort CHAT_CMD_AUTH_ACCEPTED =                0x1C00;     // Identity validated and aceepted
        public const ushort CHAT_CMD_CHANNEL_LIST =                 0x0C01;     // User requesting channel list
        public const ushort CHAT_CMD_RECV_CHANNEL_LIST =            0x1C02;     // Server replying with channel list
        public const ushort CHAT_CMD_PING =                         0x2A00;     // Used to check if a user is alive
        public const ushort CHAT_CMD_PONG =                         0x2A01;     // Used to check if a user is alive*/

        // Chat server version 5 families
        public const byte CHAT_FAMILY_OLD_VERSION =                 0x00;
        public const byte CHAT_FAMILY_C2S =                         0x0C;
        public const byte CHAT_FAMILY_S2C =                         0x1C;
        public const byte CHAT_FAMILY_CONNECTION_CONTROL =          0x2A;

        // CHAT_FAMILY_CONNECTION_CONTROL (0x2A) constants
        public const byte CHAT_CMD_PING =                           0x00;
        public const byte CHAT_CMD_PONG =                           0x01;

        // CHAT_FAMILY_C2S (0x0C) constants
        public const byte CHAT_CMD_AUTH_INFO =                      0x00;
        public const byte CHAT_CMD_CHANNEL_LIST =                   0x01;

        // CHAT_FAMILY_S2C (0x1C) constants
        public const byte CHAT_CMD_AUTH_ACCEPTED =                  0x00;
        public const byte CHAT_CMD_AUTH_DENIED =                    0x01;
        public const byte CHAT_CMD_RECV_CHANNEL_LIST =              0x02;

        public const int CHAT_SERVER_VERSION =                 0x08;

        public const byte USER_STATUS_OFFLINE =     0x00;
        public const byte USER_STATUS_ONLINE =      0x03;
        public const byte USER_STATUS_LOBBY =       0x04;
        public const byte USER_STATUS_GAME =        0x05;

        public const byte USER_FLAG_NORMAL =        0x00;
        public const byte USER_FLAG_CLAN_OFFICER =  0x01;
        public const byte USER_FLAG_CLAN_LEADER =   0x02;
        public const byte USER_FLAG_CLAN_MEMBER =   0x08; // Never seen it, so we're usurping it...
        public const byte USER_FLAG_S2 =            0x20;
        public const byte USER_FLAG_PURCHASED =     0x40;

        public const byte BUDDY_NOTIFY_ADDED =      0x01;
        public const byte BUDDY_NOTIFY_REMOVED =    0x03;

        public const byte OPER_TYPE_NONE =          0x00;
        public const byte OPER_TYPE_OFFICER =       0x01;
        public const byte OPER_TYPE_LEADER =        0x02;
        public const byte OPER_TYPE_FOUNDER =       0x03;

        //
        // Events
        //

        // Events that bubble up from the socket.
        public event SocketConnectedHandler onSocketConnected;
        public event SocketConnectingHandler onSocketConnecting;
        public event SocketDisconnectedHandler onSocketDisconnected;
        public event SocketDisconnectingHandler onSocketDisconnecting;
        public event SocketErroredHandler onSocketError;
        public event SocketReceivedDataHandler onSocketRecv;
        public event SocketSendDataHandler onSocketSend;

        // Events that bubble up from the login.
        public event HoNLoginInfoEventHandler onHoNLoginInfo;

        // Events that bubble up from the client requester.
        public event HoNClientRequesterCompletedEventHandler onClientRequestCompleted;

        // HoNClient specific events.
        public event HoNLoginEventHandler onLogin;
        public event HoNAuthAcceptedEventHandler onAuthAccepted;
        public event HoNPingEventHandler onPing;
        public event HoNChannelMessageEventHandler onChannelMessage;
        public event HoNChangedChannelEventHandler onChangedChannel;
        public event HoNJoinedChannelEventHandler onJoinedChannel;
        public event HoNLeftChannelEventHandler onLeftChannel;
        public event HoNInitialStatusEventHandler onInitialStatus;
        public event HoNWhisperEventHandler onWhisper;
        public event HoNBuddyWhisperEventHandler onBuddyWhisper;
        public event HoNUpdateStatusEventHandler onUpdateStatus;
        public event HoNNotificationEventHandler onNotification;
        public event HoNClanWhisperEventHandler onClanWhisper;
        public event HoNIMEventHandler onIM;
        public event HoNChannelListEventHandler onChannelList;
        public event HoNMaxChannelsEventHandler onMaxChannels;
        public event HoNUserInfoNoExistEventHandler onUserInfoNoExist;
        public event HoNUserInfoOfflineEventHandler onUserInfoOffline;
        public event HoNUserInfoOnlineEventHandler onUserInfoOnline;
        public event HoNUserInfoInGameEventHandler onUserInfoInGame;
        public event HoNChannelKickEventHandler onChannelKick;
        public event HoNChannelSilencedEventHandler onChannelSilenced;
        public event HoNChannelSilenceLiftedEventHandler onChannelSilenceLifted;
        public event HoNChannelSilencePlacedEventHandler onChannelSilencePlaced;
        public event HoNChannelPromoteEventHandler onChannelPromote;
        public event HoNChannelDemoteEventHandler onChannelDemote;
        public event HoNJoinChannelPasswordEventHandler onJoinChannelPassword;
        public event HoNMessageAllEventHandler onMessageAll;
        public event HoNVersionMismatchEventHandler onVersionMismatch;
        public event HoNWhisperFailedEventHandler onWhisperFailed;
        public event HoNChannelEmoteEventHandler onChannelEmote;
        public event HoNRollDiceEventHandler onRollDice;
        public event HoNClanInviteEventHandler onClanInvite;
        public event HoNNewClanMemberEventHandler onNewClanMember;
        public event HoNLookingForClanEventHandler onLookingForClan;
        public event HoNChannelTopicEventHandler onChannelTopic;
        public event HoNChannelUpdateEventHandler onChannelUpdate;
        public event HoNInvitedToServerEventHandler onInvitedToServer;
        public event HoNListedChannelEventHandler onListedChannel;
        public event HoNNameChangeEventHandler onNameChange;
        public event HoNUserCountUpdateEventHandler onUserCountUpdate;
        public event HoNBannedFromChannelEventHandler onChannelBan;
        public event HoNUnbannedFromChannelEventHandler onChannelUnban;
        public event HoNYouAreBannedFromChannelEventHandler onYouAreBanned;
        public event HoNAuthDeniedEventHandler onAuthDenied;

        //
        // Objects
        //
        private AsynchronousTCPClient _conn;
        private HoNClientLogin _login;

        private GenericBuffer HoNBuffer;
        private HoNRecvBuffer HoNRecv;
        private HoNRecvBuffer packetRecv;

        //
        // Variables
        //
        private string _username;
        private UInt32 _userid;
        private string _cookie;
        private string _host;
        private UInt16 _port;
        private string _acckey;

        public HoNClient()
        {

        }

        public string Username
        {
            get { return this._username; }
        }

        public UInt32 UserID
        {
            get { return this._userid; }
        }

        public string Cookie
        {
            get { return this._cookie; }
        }

        public string AccountKey
        {
            set { this._acckey = value; }
            get { return this._acckey; }
        }

        #region HoNClient Commands
        public void Login(string Username, string Password)
        {
            HoNBuffer = new GenericBuffer();
            HoNRecv = new HoNRecvBuffer();
            packetRecv = new HoNRecvBuffer();

            this._login = new HoNClientLogin();
            this._login.Login += new HoNLoginInfoEventHandler(_login_Login);

            this._conn = new AsynchronousTCPClient();
            this._conn.onConnected += new SocketConnectedHandler(_conn_onConnected);
            this._conn.onConnecting += new SocketConnectingHandler(_conn_onConnecting);
            this._conn.onDisconnected += new SocketDisconnectedHandler(_conn_onDisconnected);
            this._conn.onDisconnecting += new SocketDisconnectingHandler(_conn_onDisconnecting);
            this._conn.onError += new SocketErroredHandler(_conn_onError);
            this._conn.onRecv += new SocketReceivedDataHandler(_conn_onRecv);
            this._conn.onSend += new SocketSendDataHandler(_conn_onSend);

            this._login.DoLogin(Username, Password);
        }

        public void Disconnect()
        {
            if (this._conn.IsSocketConnected())
            {
                this._conn.Disconnect();
            }
        }
        #endregion


        #region HoNClientLogin Events
        //
        // HoN Login events
        //
        void _login_Login(object sender, HoNLoginInfoArgs e)
        {
            Hashtable accountInfo = (Hashtable)e.Data();

            if (e.LoggedIn() == true)
            {
                this._username = accountInfo["nickname"].ToString();
                this._userid = Convert.ToUInt32(accountInfo["account_id"]);
                this._cookie = accountInfo["cookie"].ToString();

                if (onLogin != null)
                {
                    onLogin(sender, new HoNLoginResultArgs(e.LoggedIn(), this._username));
                }

                // We've raised the event, we ought to start connecting and stuff.
                this._host = accountInfo["chat_url"].ToString();
                this._port = 11031;
                this._conn.Connect(this._host, this._port);
            }
            else
            {
                if (onLogin != null)
                {
                    onLogin(sender, new HoNLoginResultArgs(e.LoggedIn(), accountInfo["auth"].ToString()));
                }
            }

            if (onHoNLoginInfo != null)
            {
                onHoNLoginInfo(sender, e);
            }
        }
        #endregion

        #region Socket Events
        //
        // Socket events
        //

        void _conn_onSend(object sender, SocketSendEventArgs e)
        {
            if (onSocketSend != null)
            {
                onSocketSend(sender, e);
            }
        }

        void _conn_onRecv(object sender, SocketRecvEventArgs e)
        {
            packetRecv.Add(e.GetData(), e.GetBytesReceived());

            while (packetRecv.HasPacket())
            {
                byte[] tmp = packetRecv.GetPacketBytes();
                HoNRecv.Add(tmp, tmp.Length);

                Debug.WriteLine("--- Packet ---");
                Debug.WriteLine(DebugOutput(packetRecv.GetPacket()));

                // For some reason, the packet length is big endian.
                ushort packetLength = Convert.ToUInt16(HoNRecv.ExtractBigEndianWORD());
                byte packetID = HoNRecv.ExtractByte();
                byte packetFamily = HoNRecv.ExtractByte();

                this.HandlePacket(packetFamily, packetID);

                // Because only the packet is copied to the new buffer, we can advance the overall buffer
                // by its total length instead of depending on the individual parsing routines to work
                // correctly.  Of course, this can be screwed up again by them removing the packet
                // length header.

                //HoNRecv.UpdateBuffer();
                HoNRecv.Flush();

                // Throw away the packet.
                packetRecv.ExtractBytes(packetLength + 2);
                packetRecv.UpdateBuffer();
            }

            if (onSocketRecv != null)
            {
                onSocketRecv(sender, e);
            }
        }

        void _conn_onError(object sender, SocketErrorEventArgs e)
        {
            if (onSocketError != null)
            {
                onSocketError(sender, e);
            }

            if (onSocketDisconnected != null)
            {
                onSocketDisconnected(sender, new EventArgs());
            }
        }

        void _conn_onDisconnecting(object sender, EventArgs e)
        {
            if (onSocketDisconnecting != null)
            {
                onSocketDisconnecting(sender, e);
            }
        }

        void _conn_onDisconnected(object sender, EventArgs e)
        {
            if (onSocketDisconnected != null)
            {
                onSocketDisconnected(sender, e);
            }
        }

        void _conn_onConnecting(object sender, SocketConnectEventArgs e)
        {
            if (onSocketConnecting != null)
            {
                onSocketConnecting(sender, e);
            }
        }

        void _conn_onConnected(object sender, SocketConnectEventArgs e)
        {
            if (onSocketConnected != null)
            {
                onSocketConnected(sender, e);
            }

            // Send the cookie and such.
            HoNBuffer.InsertBYTE(CHAT_CMD_AUTH_INFO);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_C2S);
            HoNBuffer.InsertDWORD(this._userid);
            HoNBuffer.InsertString(this._cookie);
            HoNBuffer.InsertBYTE(0x00); // Don't know.
            HoNBuffer.InsertDWORD(CHAT_SERVER_VERSION);
            HoNBuffer.InsertDWORD(0); // Presumably invisible.
            this.Send();
        }
        #endregion

        #region All the Good Stuff™
        private void HandlePacket(byte packetFamily, byte packetID)
        {
            //Debug.WriteLine(String.Format("Handling packet from family 0x{0:X2} with ID 0x{1:X2}", packetFamily, packetID));

            switch (packetFamily)
            {
                case CHAT_FAMILY_S2C:
                    {
                        switch (packetID)
                        {
                            case CHAT_CMD_AUTH_ACCEPTED:
                                {
                                    HandleAuthAccepted();
                                    break;
                                }
                            case CHAT_CMD_AUTH_DENIED:
                                {
                                    HandleAuthDenied(HoNRecv.ExtractByte());
                                    break;
                                }
                            case CHAT_CMD_RECV_CHANNEL_LIST:
                                {
                                    // (DWORD) Chat ID
                                    // (STRING) Channel name
                                    // (WORD) User count
                                    HandleListedChannel(HoNRecv.ExtractDWORD(), HoNRecv.ExtractString(), HoNRecv.ExtractWORD());
                                    break;
                                }
                            default:
                                {
                                    Debug.WriteLine(String.Format("Unrecognized Packet ID 0x{0:X2}", packetID));
                                    Debug.WriteLine(DebugOutput(HoNRecv.GetBuffer()));

                                    throw new Exception(String.Format("Unrecognized Packet ID 0x{0:X2}", packetID) + "\r\n" + DebugOutput(HoNRecv.GetBuffer()));
                                    //HoNRecv.ExtractBytes(packetLength - 1);
                                }
                        }
                        break;
                    }
                case CHAT_FAMILY_CONNECTION_CONTROL:
                    {
                        switch (packetID)
                        {
                            case CHAT_CMD_PING:
                                {
                                    HandlePing();
                                    break;
                                }
                            default:
                                {
                                    Debug.WriteLine(String.Format("Unrecognized Packet ID 0x{0:X2}", packetID));
                                    Debug.WriteLine(DebugOutput(HoNRecv.GetBuffer()));

                                    throw new Exception(String.Format("Unrecognized Packet ID 0x{0:X2}", packetID) + "\r\n" + DebugOutput(HoNRecv.GetBuffer()));
                                    //HoNRecv.ExtractBytes(packetLength - 1);
                                }
                        }
                        break;
                    }
                case CHAT_FAMILY_OLD_VERSION:
                    {
                        switch (packetID)
                        {
                            case CHAT_CMD_CHANNEL_MSG:
                                {
                                    // (DWORD) Account ID
                                    // (DWORD) Chat ID
                                    // (NTSTRING) Message
                                    HandleChannelMessage(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANGED_CHANNEL:
                                {
                                    // (NTSTRING) Channel name
                                    // (DWORD) Chat ID
                                    // (BYTE) 0 - Probably another NTSTRING
                                    // (NTSTRING) Topic
                                    // (DWORD) Number of ops
                                    // [Oper block]
                                    // -(DWORD) Op ID
                                    // -(BYTE) Op Type
                                    // (DWORD) Number of users
                                    // [User block]
                                    // -(NTSTRING) Username
                                    // -(DWORD) User ID
                                    // -(BYTE) Status
                                    // -(BYTE) Flags
                                    HandleChangedChannel(HoNRecv.ExtractString(), HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_JOINED_CHANNEL:
                                {
                                    // (NTSTRING) Username
                                    // (DWORD) User ID
                                    // (DWORD) Chat ID
                                    // (BYTE) Status
                                    // (BYTE) Flags
                                    HandleJoinedChannel(HoNRecv.ExtractString(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractByte(), HoNRecv.ExtractByte());
                                    break;
                                }
                            case CHAT_CMD_LEFT_CHANNEL:
                                {
                                    // (DWORD) User ID
                                    // (DWORD) Chat ID
                                    HandleLeftChannel(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_WHISPER:
                                {
                                    // (NTSTRING) Username
                                    // (NTSTRING) Message
                                    HandleWhisper(HoNRecv.ExtractString(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_WHISPER_FAILED:
                                {
                                    HandleWhisperFailed();
                                    break;
                                }
                            case CHAT_CMD_INITIAL_STATUS:
                                {
                                    // (DWORD) Buddy count
                                    // [Buddy Block]
                                    // (DWORD) User ID
                                    // (BYTE) Status
                                    // (BYTE) Flags
                                    // (NTSTRING) Server
                                    // (NTSTRING) Game Name
                                    HandleInitialStatus(HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_UPDATE_STATUS:
                                {
                                    // (DWORD) User ID
                                    // (BYTE) Status
                                    // (BYTE) Flags
                                    // (DWORD) Clan ID? 0 if no clan
                                    // (NTSTRING) Clan name
                                    HandleUpdateStatus(HoNRecv.ExtractDWORD(), HoNRecv.ExtractByte(), HoNRecv.ExtractByte(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_NOTIFICATION:
                                {
                                    // (BYTE) Notification type (1 = user added you, 3 = user removed you)
                                    // (NTSTRING) Someone has added/removed you from their list.
                                    HandleNotification(HoNRecv.ExtractByte(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CLAN_WHISPER:
                                {
                                    // (DWORD) User ID
                                    // (NTSTRING) Message
                                    HandleClanWhisper(HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_IM:
                                {
                                    // (NTSTRING) Username
                                    // (NTSTRING) Message
                                    HandleIM(HoNRecv.ExtractString(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_LOOKING_FOR_CLAN:
                                {
                                    // (NTSTRING) Username
                                    HandleLookingForClan(HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_LIST:
                                {
                                    // (DWORD) Channel count
                                    // [Channel block]
                                    // (DWORD) Channel ID
                                    // (STRING) Channel name
                                    // (DWORD) User count
                                    HandleChannelList(HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_MAX_CHANNELS:
                                {
                                    HandleMaxChannels();
                                    break;
                                }
                            case CHAT_CMD_INVITED_TO_SERVER:
                                {
                                    // (NTSTRING) Username
                                    // (DWORD) User ID
                                    // (NTSTRING) Server
                                    HandleInvitedToServer(HoNRecv.ExtractString(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_USER_INFO_NO_EXIST:
                                {
                                    // (STRING) Username
                                    HandleUserInfoNoExist(HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_USER_INFO_OFFLINE:
                                {
                                    // (NTSTRING) Username
                                    // (NTSTRING) Last seen
                                    HandleUserInfoOffline(HoNRecv.ExtractString(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_USER_INFO_ONLINE:
                                {
                                    // (NTSTRING) Username
                                    // (DWORD) Number of channels
                                    // [Channel block]
                                    // (NTSTRING) Channel
                                    HandleUserInfoOnline(HoNRecv.ExtractString(), HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_USER_INFO_IN_GAME:
                                {
                                    // (NTSTRING) Username
                                    // (NTSTRING) Game name
                                    // (NTSTRING) Game time
                                    HandleUserInfoInGame(HoNRecv.ExtractString(), HoNRecv.ExtractString(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_UPDATE:
                                {
                                    // (DWORD) Chat ID
                                    // (NTSTRING) Chat name
                                    // (BYTE) 00
                                    // (NTSTRING) Topic
                                    // (DWORD) Update count?
                                    // (DWORD) User ID
                                    // (BYTE) Oper type
                                    HandleChannelUpdate(HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_TOPIC:
                                {
                                    // (DWORD) Chat ID
                                    // (NTSTRING) Topic
                                    HandleChannelTopic(HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_KICK:
                                {
                                    // (DWORD) Chat ID
                                    // (DWORD) Kicker ID
                                    // (DWORD) Kicked ID
                                    HandleChannelKick(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_SILENCED:
                                {
                                    // (DWORD) Chat ID
                                    HandleChannelSilenced(HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_SILENCE_LIFTED:
                                {
                                    // (NTSTRING) Channel name
                                    HandleChannelSilenceLifted(HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_SILENCE_PLACED:
                                {
                                    // (NTSTRING) Channel name
                                    // (NTSTRING) Muter name
                                    // (NTSTRING) Muted name
                                    // (DWORD) Duration in milliseconds
                                    HandleChannelSilencePlaced(HoNRecv.ExtractString(), HoNRecv.ExtractString(), HoNRecv.ExtractString(), HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_BAN:
                                {
                                    // (DWORD) Chat ID
                                    // (DWORD) Oper ID
                                    // (NTSTRING) Username
                                    HandleChannelBan(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_UNBAN:
                                {
                                    // (DWORD) Chat ID
                                    // (DWORD) Oper ID
                                    // (NTSTRING) Username
                                    HandleChannelUnban(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_IS_BANNED:
                                {
                                    // (NTSTRING) Channel name
                                    HandleYouAreBannedFromChannel(HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_MESSAGE_ALL:
                                {
                                    // (NTSTRING) Username
                                    // (NTSTRING) Message
                                    HandleMessageAll(HoNRecv.ExtractString(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_PROMOTE:
                                {
                                    // (DWORD) Chat ID
                                    // (DWORD) Target ID
                                    // (DWORD) Oper ID
                                    HandleChannelPromote(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_CHANNEL_DEMOTE:
                                {
                                    // (DWORD) Chat ID
                                    // (DWORD) Target ID
                                    // (DWORD) Oper ID
                                    HandleChannelDemote(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_VERSION_MISMATCH:
                                {
                                    HandleVersionMismatch();
                                    break;
                                }
                            case CHAT_CMD_JOIN_CHANNEL_PASSWORD:
                                {
                                    // (NTSTRING) Channel name
                                    HandleJoinChannelPassword(HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_CLAN_ADD_MEMBER:
                                {
                                    // (NTSTRING) Inviter
                                    // (NTSTRING) Clan Name
                                    HandleClanInvite(HoNRecv.ExtractString(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_NEW_CLAN_MEMBER:
                                {
                                    // (DWORD) User ID
                                    HandleNewClanMember(HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_ROLL_DICE:
                                {
                                    // (DWORD) User ID
                                    // (DWORD) Chat ID
                                    // (NTSTRING) Message
                                    HandleChannelRollDice(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_EMOTE:
                                {
                                    // (DWORD) User ID
                                    // (DWORD) Chat ID
                                    // (NTSTRING) Message
                                    HandleChannelEmote(HoNRecv.ExtractDWORD(), HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_USER_COUNT_UPDATE:
                                {
                                    // (DWORD) The number of users
                                    HandleUpdateUserCount(HoNRecv.ExtractDWORD());
                                    break;
                                }
                            case CHAT_CMD_NAME_CHANGE:
                                {
                                    // (DWORD) User ID
                                    // (NTSTRING) Username
                                    HandleNameChange(HoNRecv.ExtractDWORD(), HoNRecv.ExtractString());
                                    break;
                                }
                            case CHAT_CMD_WHISPER_BUDDIES:
                                {
                                    // (NTSTRING) Username
                                    // (NTSTRING) Message
                                    HandleBuddyWhisper(HoNRecv.ExtractString(), HoNRecv.ExtractString());
                                    break;
                                }
                            default:
                                {
                                    // As of 0.1.66.1, the DWORD preface no longer exists.  Unrecognized packet IDs must be advanced to the next
                                    // packet boundary, or there will be a misalignment!

                                    // The 0.3.x.x series of patches added a big endian packet length preamble.

                                    Debug.WriteLine(String.Format("Unrecognized Packet ID 0x{0:X2}", packetID));
                                    Debug.WriteLine(DebugOutput(HoNRecv.GetBuffer()));

                                    throw new Exception(String.Format("Unrecognized Packet ID 0x{0:X2}", packetID) + "\r\n" + DebugOutput(HoNRecv.GetBuffer()));
                                    //HoNRecv.ExtractBytes(packetLength - 1);
                                }
                        }
                        break;
                    }
                default:
                    {
                        Debug.WriteLine(String.Format("Unrecognized Packet Family 0x{0:X2}", packetFamily));
                        Debug.WriteLine(DebugOutput(HoNRecv.GetBuffer()));

                        throw new Exception(String.Format("Unrecognized Packet Family 0x{0:X2}", packetFamily) + "\r\n" + DebugOutput(HoNRecv.GetBuffer()));
                        //HoNRecv.ExtractBytes(packetLength - 1);
                    }
            }
        }

        private void HandleYouAreBannedFromChannel(string Channel)
        {
            if (onYouAreBanned != null)
            {
                this.onYouAreBanned(this, new HoNYouAreBannedFromChannelArgs(Channel));
            }
        }

        private void HandleChannelUnban(UInt32 ChatID, UInt32 OperID, string Username)
        {
            if (onChannelUnban != null)
            {
                this.onChannelUnban(this, new HoNUnbannedFromChannelArgs(ChatID, OperID, Username));
            }
        }

        private void HandleChannelBan(UInt32 ChatID, UInt32 OperID, string Username)
        {
            if (onChannelBan != null)
            {
                this.onChannelBan(this, new HoNBannedFromChannelArgs(ChatID, OperID, Username));
            }
        }

        private void HandleUpdateUserCount(UInt32 UserCount)
        {
            if (onUserCountUpdate != null)
            {
                this.onUserCountUpdate(this, new HoNUserCountUpdateArgs(UserCount));
            }
        }

        private void HandleAuthDenied(byte Reason)
        {
            string _reason;

            switch (Reason)
            {
                case 0x01:
                    {
                        // Invalid version
                        _reason = "Chat server authorization denied: Invalid version";
                        break;
                    }
                default:
                    {
                        // Unknown
                        _reason = "Chat server authorization denied: Unknown reason";
                        break;
                    }
            }

            if (onAuthDenied != null) onAuthDenied(this, new HoNAuthDeniedArgs(_reason));
        }

        private void HandleBuddyWhisper(string Username, string Message)
        {
            if (onBuddyWhisper != null)
            {
                onBuddyWhisper(this, new HoNBuddyWhisperArgs(Username, Message));
            }
        }

        private void HandleNameChange(UInt32 UserID, string Username)
        {
            if (onNameChange != null)
            {
                onNameChange(this, new HoNNameChangeArgs(UserID, Username));
            }
        }

        private void HandleInvitedToServer(string Username, uint UserID, string Server)
        {
            if (onInvitedToServer != null)
            {
                onInvitedToServer(this, new HoNInvitedToServerArgs(Username, UserID, Server));
            }
        }

        private void HandleListedChannel(UInt32 ChatID, string Channel, UInt16 UserCount)
        {
            if (onListedChannel != null)
            {
                onListedChannel(this, new HoNListedChannelArgs(ChatID, Channel, UserCount));
            }
        }

        private void HandleChannelUpdate(UInt32 ChatID, string Channel)
        {
            // Throw away that one byte...
            HoNRecv.ExtractByte();
            // (NTSTRING) Topic
            // (DWORD) Update count?
            // (DWORD) User ID
            // (BYTE) Oper type
            string Topic = HoNRecv.ExtractString();
            UInt32 count = HoNRecv.ExtractDWORD();

            HoNChannelUpdateArgs hcu = new HoNChannelUpdateArgs(Channel, ChatID, Topic);
            hcu.SetNumOpers(count);
            
            for (int i = 0; i < count; i++)
            {
                UInt32 OperID = HoNRecv.ExtractDWORD();
                byte OperType = HoNRecv.ExtractByte();
                hcu.AddOper(i, OperID, OperType);
            }

            if (onChannelUpdate != null)
            {
                onChannelUpdate(this, hcu);
            }
        }

        private void HandleChannelTopic(UInt32 ChatID, string Topic)
        {
            if (onChannelTopic != null)
            {
                onChannelTopic(this, new HoNChannelTopicArgs(ChatID, Topic));
            }
        }

        private void HandleLookingForClan(string Username)
        {
            if (onLookingForClan != null)
            {
                onLookingForClan(this, new HoNLookingForClanArgs(Username));
            }
        }

        private void HandleAuthAccepted()
        {
            if (onAuthAccepted != null)
            {
                onAuthAccepted(this, new HoNAuthAcceptedArgs());
            }
        }

        private void HandlePing()
        {
            SendPong();

            if (onPing != null)
            {
                onPing(this, new HoNPingArgs());
            }
        }

        private void HandleChannelMessage(UInt32 UserID, UInt32 ChatID, string Message)
        {
            if (onChannelMessage != null)
            {
                onChannelMessage(this, new HoNChannelMessageArgs(UserID, ChatID, Message));
            }
        }

        private void HandleChannelEmote(UInt32 UserID, UInt32 ChatID, string Message)
        {
            if (onChannelEmote != null)
            {
                onChannelEmote(this, new HoNChannelEmoteArgs(UserID, ChatID, Message));
            }
        }

        private void HandleChannelRollDice(UInt32 UserID, UInt32 ChatID, string Message)
        {
            if (onRollDice != null)
            {
                onRollDice(this, new HoNRollDiceArgs(UserID, ChatID, Message));
            }
        }

        private void HandleChangedChannel(string ChannelName, UInt32 ChatID)
        {
            // Throw away a byte.  That byte might be another string though.
            HoNRecv.ExtractByte();
            string strTopic = HoNRecv.ExtractString();

            HoNChangedChannelArgs hjc = new HoNChangedChannelArgs(ChannelName, ChatID, strTopic);

            uint opCount = HoNRecv.ExtractDWORD();
            hjc.SetNumOpers(opCount);
            for (int i = 0; i < opCount; i++)
            {
                UInt32 OperID = HoNRecv.ExtractDWORD();
                byte OperType = HoNRecv.ExtractByte();
                hjc.AddOper(i, OperID, OperType);
            }

            string Username;
            UInt32 UserID;
            byte userStatus;
            byte userFlags;
            uint userCount = HoNRecv.ExtractDWORD();
            hjc.SetNumUsers(userCount);
            for (int i = 0; i < userCount; i++)
            {
                Username = HoNRecv.ExtractString();
                UserID = HoNRecv.ExtractDWORD();
                userStatus = HoNRecv.ExtractByte();
                userFlags = HoNRecv.ExtractByte();

                // Hack for clan membership
                if (Regex.IsMatch(Username, @"\[[^]]+]"))
                {
                    userFlags = Convert.ToByte(userFlags | HoNClient.USER_FLAG_CLAN_MEMBER);
                }

                hjc.AddUser(i, Username, UserID, userStatus, userFlags);
            }

            if (onChangedChannel != null)
            {
                onChangedChannel(this, hjc);
            }
        }

        private void HandleJoinedChannel(string Username, UInt32 UserID, UInt32 ChatID, byte Status, byte Flags)
        {
            // Hack for clan membership
            if (Regex.IsMatch(Username, @"\[[^]]+]"))
            {
                Flags = Convert.ToByte(Flags | HoNClient.USER_FLAG_CLAN_MEMBER);
            }

            if (onJoinedChannel != null)
            {
                onJoinedChannel(this, new HoNJoinedChannelArgs(Username, UserID, ChatID, Status, Flags));
            }
        }

        private void HandleLeftChannel(UInt32 UserID, UInt32 ChatID)
        {
            if (onLeftChannel != null)
            {
                onLeftChannel(this, new HoNLeftChannelArgs(UserID, ChatID));
            }
        }

        private void HandleWhisper(string Username, string Message)
        {
            if (onWhisper != null)
            {
                onWhisper(this, new HoNWhisperArgs(Username, Message));
            }
        }

        private void HandleWhisperFailed()
        {
            if (onWhisperFailed != null)
            {
                onWhisperFailed(this, new EventArgs());
            }
        }

        private void HandleInitialStatus(UInt32 BuddyCount)
        {
            HoNInitialStatusArgs hbl = new HoNInitialStatusArgs(BuddyCount);

            HoNBuddy b;
            for (int i = 0; i < BuddyCount; i++)
            {
                b.UserID = HoNRecv.ExtractDWORD();
                b.Status = HoNRecv.ExtractByte();
                b.Flags = HoNRecv.ExtractByte();

                if (b.Status == USER_STATUS_LOBBY || b.Status == USER_STATUS_GAME)
                {
                    b.Server = HoNRecv.ExtractString();
                }
                else
                {
                    b.Server = String.Empty;
                }

                if (b.Status == USER_STATUS_GAME || b.Status == USER_STATUS_LOBBY)
                {
                    b.GameName = HoNRecv.ExtractString();
                }
                else
                {
                    b.GameName = String.Empty;
                }

                hbl.AddBuddy(i, b.UserID, b.Status, b.Flags, b.Server, b.GameName);

                Debug.WriteLine(String.Format("Buddy #{0}: ID #{1} - Status: 0x{2:X2} - Flags: 0x{3:X2} - Server: {4} - Game: {5}", i + 1, b.UserID, b.Status, b.Flags, b.Server, b.GameName));
            }

            if (onInitialStatus != null)
            {
                onInitialStatus(this, hbl);
            }
        }

        private void HandleUpdateStatus(UInt32 UserID, byte Status, byte Flags, UInt32 ClanID, string ClanName)
        {
            string GameServer = "";
            string GameName = "";
            UInt32 GameID = 0;

            // Hack to support clan membership.
            if (ClanID != 0)
            {
                Flags = Convert.ToByte(Flags | HoNClient.USER_FLAG_CLAN_MEMBER);
            }

            // Flags: 0x40 - prepurchased, 0x01 - moderator, 0x02 - founder

            switch (Status)
            {
                case USER_STATUS_OFFLINE: // User is offline
                    {
                        break;
                    }
                case USER_STATUS_ONLINE: // User is online
                    {
                        break;
                    }
                case USER_STATUS_LOBBY: // User is in a game lobby
                    {
                        GameServer = HoNRecv.ExtractString();
                        break;
                    }
                case USER_STATUS_GAME: // User is in a game
                    {
                        GameServer = HoNRecv.ExtractString();
                        GameName = HoNRecv.ExtractString();
                        GameID = HoNRecv.ExtractDWORD();
                        break;
                    }
                default:
                    {
                        break;
                    }
            }

            if (onUpdateStatus != null)
            {
                onUpdateStatus(this, new HoNUpdateStatusArgs(UserID, Status, Flags, ClanID, ClanName, GameServer, GameName, GameID));
            }
        }

        private void HandleNotification(byte notifyType, string Message)
        {
            string[] splitMessage = Message.Split(' ');
            string strUser = splitMessage[0];
            switch (notifyType)
            {
                case BUDDY_NOTIFY_ADDED: // You were added.
                    {
                        //Debug.WriteLine(strUser + " has added you as a buddy.");
                        break;
                    }
                case BUDDY_NOTIFY_REMOVED: // You were deleted.
                    {
                        //Debug.WriteLine(strUser + " has removed you from their buddy list.");
                        break;
                    }
                default: // Dunno.
                    {
                        Debug.WriteLine(String.Format("There was a buddy notification of unknown type 0x{0:X2} with message {1}", notifyType, Message));
                        break;
                    }
            }

            if (onNotification != null)
            {
                onNotification(this, new HoNNotificationArgs(notifyType, Message));
            }
        }

        private void HandleClanWhisper(UInt32 UserID, string Message)
        {
            if (onClanWhisper != null)
            {
                onClanWhisper(this, new HoNClanWhisperArgs(UserID, Message));
            }
        }

        private void HandleIM(string Username, string Message)
        {
            if (onIM != null)
            {
                onIM(this, new HoNIMArgs(Username, Message));
            }
        }

        private void HandleChannelList(UInt32 ChannelCount)
        {
            HoNChannelListArgs hcl = new HoNChannelListArgs(ChannelCount);

            HoNChannel c;
            for (int i = 0; i < ChannelCount; i++)
            {
                c.ChatID = HoNRecv.ExtractDWORD();
                c.Channel = HoNRecv.ExtractString();
                c.UserCount = HoNRecv.ExtractDWORD();

                hcl.AddChannel(i, c.ChatID, c.Channel, c.UserCount);
            }

            if (onChannelList != null)
            {
                onChannelList(this, hcl);
            }
        }

        private void HandleMaxChannels()
        {
            if (onMaxChannels != null)
            {
                onMaxChannels(this, new HoNNoMoreChannelsArgs());
            }
        }

        private void HandleUserInfoNoExist(string Username)
        {
            if (onUserInfoNoExist != null)
            {
                onUserInfoNoExist(this, new HoNUserInfoNoExistArgs(Username));
            }
        }

        private void HandleUserInfoOffline(string Username, string LastSeen)
        {
            if (onUserInfoOffline != null)
            {
                onUserInfoOffline(this, new HoNUserInfoOfflineArgs(Username, LastSeen));
            }
        }

        private void HandleUserInfoOnline(string Username, UInt32 ChannelCount)
        {
            HoNUserInfoOnlineArgs huic = new HoNUserInfoOnlineArgs(Username, ChannelCount);
            string[] strChannels = new string[ChannelCount];
            for (int i = 0; i < ChannelCount; i++)
            {
                strChannels[i] = HoNRecv.ExtractString();
                huic.AddChannel(i, strChannels[i]);
            }
            string channels = String.Join("', '", strChannels);

            if (onUserInfoOnline != null)
            {
                onUserInfoOnline(this, huic);
            }
        }

        private void HandleUserInfoInGame(string Username, string GameName, string GameTime)
        {
            if (onUserInfoInGame != null)
            {
                onUserInfoInGame(this, new HoNUserInfoInGameArgs(Username, GameName, GameTime));
            }
        }

        private void HandleChannelKick(UInt32 ChatID, UInt32 KickerID, UInt32 KickedID)
        {
            if (onChannelKick != null)
            {
                onChannelKick(this, new HoNChannelKickArgs(ChatID, KickerID, KickedID));
            }
        }

        private void HandleChannelSilenced(UInt32 ChatID)
        {
            if (onChannelSilenced != null)
            {
                onChannelSilenced(this, new HoNChannelSilencedArgs(ChatID));
            }
        }

        private void HandleChannelSilenceLifted(string ChannelName)
        {
            if (onChannelSilenceLifted != null)
            {
                onChannelSilenceLifted(this, new HoNChannelSilenceLiftedArgs(ChannelName));
            }
        }

        private void HandleChannelSilencePlaced(string ChannelName, string MuterName, string MutedName, UInt32 Duration)
        {
            if (onChannelSilencePlaced != null)
            {
                onChannelSilencePlaced(this, new HoNChannelSilencePlacedArgs(ChannelName, MuterName, MutedName, Duration));
            }
        }

        private void HandleChannelPromote(UInt32 ChatID, UInt32 TargetID, UInt32 OperID)
        {
            if (onChannelPromote != null)
            {
                onChannelPromote(this, new HoNChannelPromoteArgs(ChatID, TargetID, OperID));
            }
        }

        private void HandleChannelDemote(UInt32 ChatID, UInt32 TargetID, UInt32 OperID)
        {
            if (onChannelDemote != null)
            {
                onChannelDemote(this, new HoNChannelDemoteArgs(ChatID, TargetID, OperID));
            }
        }

        private void HandleJoinChannelPassword(string ChannelName)
        {
            if (onJoinChannelPassword != null)
            {
                onJoinChannelPassword(this, new HoNJoinChannelPasswordArgs(ChannelName));
            }
        }

        private void HandleClanInvite(string Username, string ClanName)
        {
            if (onClanInvite != null)
            {
                onClanInvite(this, new HoNClanInviteArgs(Username, ClanName));
            }
        }

        private void HandleNewClanMember(UInt32 UserID)
        {
            if (onNewClanMember != null)
            {
                onNewClanMember(this, new HoNNewClanMemberArgs(UserID));
            }
        }

        private void HandleMessageAll(string Username, string Message)
        {
            if (onMessageAll != null)
            {
                onMessageAll(this, new HoNMessageAllArgs(Username, Message));
            }
        }

        private void HandleVersionMismatch()
        {
            if (onVersionMismatch != null)
            {
                onVersionMismatch(this, new EventArgs());
            }
        }
        #endregion

        #region General Utility Functions
        private void Send()
        {
            if (this._conn.IsSocketConnected())
            {
                Debug.Print("--- SEND ---");
                Debug.Print(DebugOutput(HoNBuffer.GetBuffer()));
                //Debug.Print("--- END SEND ---");
                this._conn.Send(HoNBuffer.GetBytes(), HoNBuffer.GetLength());
                HoNBuffer.Flush();
            }
        }

        public void SendPong()
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_PONG);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_CONNECTION_CONTROL);
            Send();
        }

        public void JoinChannel(string ChannelName)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_JOIN_CHANNEL);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(ChannelName);
            Send();
        }

        public void SendJoiningGame(string Server, string Port)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_JOINING_GAME);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Server + ":" + Port);
            Send();
        }

        public void SendJoinedGame(string GameName, UInt32 Unk01)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_JOINED_GAME);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(GameName);
            HoNBuffer.InsertDWORD(Unk01);
            Send();
        }

        public void JoinPasswordedChannel(string ChannelName, string Password)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_JOIN_CHANNEL_PASSWORD);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(ChannelName);
            HoNBuffer.InsertString(Password);
            Send();
        }

        public void PartChannel(string ChannelName)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_LEAVE_CHANNEL);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(ChannelName);
            Send();
        }

        public void KickFromChannel(UInt32 ChatID, UInt32 UserID)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CHANNEL_KICK);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertDWORD(ChatID);
            HoNBuffer.InsertDWORD(UserID);
            Send();
        }

        public void BanFromChannel(UInt32 ChatID, string Username)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CHANNEL_BAN);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertDWORD(ChatID);
            HoNBuffer.InsertString(Username);
            Send();
        }

        public void UnbanFromChannel(UInt32 ChatID, string Username)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CHANNEL_UNBAN);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertDWORD(ChatID);
            HoNBuffer.InsertString(Username);
            Send();
        }

        public void SilenceInChannel(UInt32 ChatID, string Username, UInt32 Duration)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CHANNEL_SILENCE_USER);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertDWORD(ChatID);
            HoNBuffer.InsertString(Username);
            HoNBuffer.InsertDWORD(Duration);
            Send();
        }

        public void PromoteInChannel(UInt32 ChatID, UInt32 UserID)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CHANNEL_PROMOTE);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertDWORD(ChatID);
            HoNBuffer.InsertDWORD(UserID);
            Send();
        }

        public void DemoteInChannel(UInt32 ChatID, UInt32 UserID)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CHANNEL_DEMOTE);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertDWORD(ChatID);
            HoNBuffer.InsertDWORD(UserID);
            Send();
        }

        public void SendChannelMessage(UInt32 ChatID, string Message)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CHANNEL_MSG);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Message);
            HoNBuffer.InsertDWORD(ChatID);
            Send();
        }

        public void SendChannelEmote(UInt32 ChatID, string Message)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_EMOTE);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Message);
            HoNBuffer.InsertDWORD(ChatID);
            Send();
        }

        public void SendChannelRoll(UInt32 ChatID, string Message)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_ROLL_DICE);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Message);
            HoNBuffer.InsertDWORD(ChatID);
            Send();
        }

        public void SendWhisper(string Username, string Message)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_WHISPER);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Username);
            HoNBuffer.InsertString(Message);
            Send();
        }

        public void SendIM(string Username, string Message)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_IM);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Username);
            HoNBuffer.InsertString(Message);
            Send();
        }

        public void SendWhois(string Username)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_USER_INFO);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Username);
            Send();
        }

        public void SendChannelListRequest()
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CHANNEL_LIST);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_C2S);
            Send();
        }

        public void SendClanMessage(string Message)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CLAN_WHISPER);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Message);
            Send();
        }

        public void SendClanInvite(string Username)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CLAN_ADD_MEMBER);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Username);
            Send();
        }

        public void SendRemoveBuddy(UInt32 UserID)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_BUDDY_REMOVE_NOTIFY);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertDWORD(UserID);
            // (DWORD) Unknown
            // (DWORD) Unknown + 1
            Send();
        }

        public void SendAddBuddy(UInt32 UserID)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_BUDDY_ADD_NOTIFY);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertDWORD(UserID);
            // (DWORD) Unknown
            // (DWORD) Unknown + 1
            Send();
        }

        public void SendMessageAll(string Message)
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_MESSAGE_ALL);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            HoNBuffer.InsertString(Message);
            Send();
        }

        public void DeclineClanInvitation()
        {
            HoNBuffer.InsertBYTE(CHAT_CMD_CLAN_ADD_REJECTED);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            Send();
        }

        public void AcceptClanInvitation()
        {
            HoNBuffer.InsertWORD(CHAT_CMD_CLAN_ADD_ACCEPTED);
            HoNBuffer.InsertBYTE(CHAT_FAMILY_OLD_VERSION);
            Send();
        }

        public void ClientRequester(string func, string index, string[] args)
        {
            string postdata = "f=" + func;
            switch (func)
            {
                case CLIENT_REQUEST_BANLIST_ADD:
                    {
                        postdata += "&account_id=";
                        postdata += args[0];
                        postdata += "&banned_id=";
                        postdata += args[1];
                        postdata += "&reason=";
                        postdata += args[2];
                        postdata += "&cookie=";
                        postdata += args[3];
                        break;
                    }
                case CLIENT_REQUEST_BANLIST_DEL:
                    {
                        postdata += "&account_id=";
                        postdata += args[0];
                        postdata += "&banned_id=";
                        postdata += args[1];
                        postdata += "&cookie=";
                        postdata += args[2];
                        break;
                    }
                case CLIENT_REQUEST_BUDDY_ADD:
                case CLIENT_REQUEST_BUDDY_DEL:
                    {
                        postdata += "&account_id=";
                        postdata += args[0];
                        postdata += "&buddy_id=";
                        postdata += args[1];
                        postdata += "&cookie=";
                        postdata += args[2];
                        break;
                    }
                case CLIENT_REQUEST_ID2NICK:
                    {
                        for (int i = 0; i < args.GetLength(0); i++)
                        {
                            postdata += "&account_id[" + i.ToString() + "]=";
                            postdata += args[i];
                        }
                        break;
                    }
                case CLIENT_REQUEST_IGNORE_ADD:
                    {
                        postdata += "&account_id=";
                        postdata += args[0];
                        postdata += "&ignored_id=";
                        postdata += args[1];
                        postdata += "&reason=";
                        postdata += args[2];
                        postdata += "&cookie=";
                        postdata += args[3];
                        break;
                    }
                case CLIENT_REQUEST_IGNORE_DEL:
                    {
                        postdata += "&account_id=";
                        postdata += args[0];
                        postdata += "&ignored_id=";
                        postdata += args[1];
                        postdata += "&cookie=";
                        postdata += args[2];
                        break;
                    }
                case CLIENT_REQUEST_NICK2ID:
                    {
                        for (int i = 0; i < args.GetLength(0); i++)
                        {
                            postdata += "&nickname[" + i.ToString() + "]=";
                            postdata += args[i];
                        }
                        break;
                    }
                case CLIENT_REQUEST_SERVER_LIST:
                    {
                        postdata += "&cookie=";
                        postdata += args[0];
                        postdata += "&gametype=";
                        postdata += args[1];

                        if (args.Length > 2)
                        {
                            postdata += "&region=";
                            postdata += args[2];
                        }
                        break;
                    }
                case CLIENT_REQUEST_STATS:
                    {
                        for (int i = 0; i < args.GetLength(0); i++)
                        {
                            postdata += "&account_id[" + i.ToString() + "]=";
                            postdata += args[i];
                        }
                        break;
                    }
                case CLIENT_REQUEST_ADD_ROOM:
                    {
                        postdata += "&account_id=";
                        postdata += args[0];
                        postdata += "&chatroom_name=";
                        postdata += args[1];
                        postdata += "&cookie=";
                        postdata += args[2];
                        break;
                    }
                case CLIENT_REQUEST_REMOVE_ROOM:
                    {
                        postdata += "&account_id=";
                        postdata += args[0];
                        postdata += "&chatroom_name=";
                        postdata += args[1];
                        postdata += "&cookie=";
                        postdata += args[2];
                        break;
                    }
            }

            HoNClientRequester hcr = new HoNClientRequester(func, index, postdata);
            hcr.onClientRequesterCompleted += new HoNClientRequesterCompletedEventHandler(hcr_onClientRequesterCompleted);
        }

        void hcr_onClientRequesterCompleted(object sender, HoNClientRequesterEventArgs e)
        {
            if (onClientRequestCompleted != null)
            {
                onClientRequestCompleted(sender, e);
            }
        }

        // Not my function, public domain use.
        public string DebugOutput(string Data)
        {
            string rtnHex = "";
            string rtnAsc = "";
            int limit = 0;
            int bytesPerLine = 16;
            for (int i = 0; i < Data.Length; i++)
            {
                //Limit bytes per line to maxlimit
                if (limit == bytesPerLine)
                {
                    limit = 0;
                    rtnHex += "\n";
                    rtnAsc += "\n";
                }
                //Add the data to the output
                rtnHex += ((byte)Convert.ToChar(Data.Substring(i, 1))).ToString("X").PadLeft(2, '0') + " ";
                rtnAsc += ChrFormat(Convert.ToChar(Data.Substring(i, 1)));
                //Increment limit
                limit++;
            }
            //Split lines
            char[] delimiter = new char[] { '\n' };
            string[] split1 = rtnHex.Split(delimiter);
            string[] split2 = rtnAsc.Split(delimiter);
            string rtnStr = "";
            //Rebuild the returnstr
            for (int i = split1.GetLowerBound(0); i <= split1.GetUpperBound(0); i++)
            {
                //Check length of split1
                while (split1[i].Length < (3 * bytesPerLine)) { split1[i] += "   "; }
                //Check length of split2
                while (split2[i].Length < bytesPerLine) { split2[i] += "."; }
                rtnStr += (i * 16).ToString("X").PadLeft(4, '0') + "  " + split1[i] + "    " + split2[i] + "\n";
            }
            //Return returnstr
            return rtnStr;
        }

        private char ChrFormat(char CHR)
        {
            if ((byte)CHR < 32) { return '.'; }
            //else if (((byte)CHR > 127) && ((byte)CHR < 159)) { return '.'; }
            else if ((byte)CHR > 127) { return '.'; }
            else { return CHR; }
        }
        #endregion
    }
}